package cn.wizzer.app.web.modules.controllers.user;

import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jsms.api.ValidSMSResult;
import cn.jsms.api.common.SMSClient;
import cn.wizzer.app.sys.modules.models.SysParam;
import cn.wizzer.app.sys.modules.services.SysParamService;
import cn.wizzer.app.sys.modules.services.SysSensitiveService;
import cn.wizzer.app.user.modules.models.User;
import cn.wizzer.app.user.modules.models.enums.EquipmentEnum;
import cn.wizzer.app.user.modules.services.UserLogService;
import cn.wizzer.app.user.modules.services.UserService;
import cn.wizzer.app.web.commons.constant.ApiConstant;
import cn.wizzer.app.web.commons.filter.ApiTokenFilter;
import cn.wizzer.app.web.commons.ulog.annotation.ULog;
import cn.wizzer.app.web.commons.utils.TokenUtilImpl;
import cn.wizzer.framework.base.Result;
import cn.wizzer.framework.filter.ParamFilter;
import com.alibaba.dubbo.config.annotation.Reference;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.EnumUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.util.ByteSource;
import org.nutz.dao.Cnd;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.Mvcs;
import org.nutz.mvc.annotation.*;
import org.nutz.mvc.impl.AdaptorErrorContext;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import redis.clients.jedis.ScanParams;

import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


@Api(value = "/api/user", tags = "用户模块")
@IocBean
@At("/api/user")
public class ApiUserController {
    private final static Log log = Logs.get();

    @Inject
    private PropertiesProxy conf;

    @Reference
    @Inject
    private UserService userService;
    @Reference
    @Inject
    private UserLogService userLogService;

    @Reference
    @Inject
    private SysParamService sysParamService;

    @Inject
    private TokenUtilImpl tokenUtil;

    @Inject
    private RedisService redisService;

    @Inject
    @Reference
    private SysSensitiveService sysSensitiveService;

    @ApiOperation(value = "用户密码登录", notes = "用户密码登录,手机号phone密码password,返回apiId,token", httpMethod = "POST", response = Object.class)
    @At("/login")
    @Ok("json:full")
    @ULog(tag = "用户登录", msg = "手机号: ${phone}, 登录设备: ${equipment}")
    public Result login(@ApiParam(required = true, value = "phone") @Param("phone") String phone,
                        @ApiParam(required = true, value = "password") @Param("password") String password,
                        @Param(value = "equipment", df = "MOBILE") EquipmentEnum equipment) {

        return Result.success("登录成功", userService.login(phone, password, equipment, Lang.getIP(Mvcs.getReq())));
    }

    /**
     * 根据用户id修改密码
     *
     * @param newPassword
     * @param password
     * @return
     */
    @ApiOperation(value = "用户根据id,原密码修改", notes = "返回确认信息", httpMethod = "POST")
    @At("/revisePassWord")
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    @ULog(tag = "用户修改密码")
    public Result revisePassword(@Param("newPassword") String newPassword,
                                 @Param("password") String password) {
        String id = Mvcs.getReq().getHeader("appid");
        String token = Mvcs.getReq().getHeader("token");

        /*
         * 校验原密码是否正确
         * 修改密码
         * */
        try {
            User user = userService.fetch(Cnd.where("id", "=", Strings.trim(id)));
            if (Strings.isBlank(password)) {
                return Result.error("请输入原密码");
            }
            String encryption = new Sha256Hash(password, ByteSource.Util.bytes(user.getSalt()), 1024).toHex();
            if (!encryption.equals(user.getPassword())) {
                return Result.error("密码验证错误");
            }
            //对密码进行加密后修改
            String salt = R.UU32();
            user.setSalt(salt);
            user.setPassword(new Sha256Hash(newPassword, ByteSource.Util.bytes(salt), 1024).toHex());
            userService.updateIgnoreNull(user);
            return Result.success("密码修改成功");
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return Result.error("密码修改错误");
    }

    /**
     * 根据用户id修改头像
     *
     * @param
     * @return
     */
    @AdaptBy(type = UploadAdaptor.class, args = {"ioc:imageUpload"})
    @ApiOperation(value = "用户头像上传", notes = "", httpMethod = "POST")
    @ULog(tag = "用户上传头像")
    @At("/revisePicture")
    @Ok("json")
    public Result revisePicture(@Param("Filedata") TempFile tf, AdaptorErrorContext err) {
        String id = Mvcs.getReq().getHeader("appid");
        String token = Mvcs.getReq().getHeader("token");
        String equipment = Mvcs.getReq().getHeader("equipment");
        try {
            if (!tokenUtil.verifyToken(id, token, EnumUtils.getEnum(EquipmentEnum.class, equipment))) {
                return Result.error(-2, "token失效,请重新获取");
            }
            if (err != null && err.getAdaptorErr() != null) {
                return Result.error("文件不合法");
            } else if (tf == null) {
                return Result.error("空文件");
            } else {
                File f = tf.getFile();                       // 这个是保存的临时文件
                String fileName = f.getName();
                String fileTyle = fileName.substring(fileName.lastIndexOf("."), fileName.length());
                fileName = R.UU32() + fileTyle;
                String toPath = ApiConstant.APP_UPLOAD_BASE + fileName;
                // 移动文件
                f.renameTo(new File(toPath));
                //文件上传成功,修改数据库
                User user = userService.fetch(Cnd.where("id", "=", Strings.trim(id)));
                user.setHeadPicture(fileName);
                userService.update(user);
                NutMap resmap = new NutMap();
                resmap.addv("HeadPicture", fileName);
                return Result.success("头像修改成功", resmap);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Result.error("系统错误");
        } catch (Throwable e) {
            return Result.error("文件格式错误");
        }
    }

    /**
     * 根据用户id修改昵称
     *
     * @param nickName
     * @return
     */
    @ApiOperation(value = "用户昵称修改", notes = " ", httpMethod = "POST")
    @At("/reviseNickName")
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    @ULog(tag = "用户修改昵称")
    public Result reviseNickName(@Param("nickName") String nickName) {
        try {
            String appid = Mvcs.getReq().getHeader("appid");
            if (!userService.checkNickName(nickName)) {
                return Result.error("昵称不符合规范!");
            }
            User user = userService.fetch(Cnd.where("id", "=", Strings.trim(appid)));
            //判断昵称是否重复
            int num = userService.count(Cnd.where("nickName", "=", Strings.trim(nickName)));
            if (num > 0) {
                return Result.error("昵称已存在!");
            }
            //敏感词过滤
            user.setNickName(sysSensitiveService.filter(nickName));
            userService.update(user);
            return Result.success("昵称修改成功");
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return Result.error("昵称修改失败");
    }

    /**
     * 根据用户id修改签名
     *
     * @param sign String
     * @return Result
     */
    @ApiOperation(value = "用户签名修改", notes = " ", httpMethod = "POST")
    @At("/reviseSign")
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    @ULog(tag = "用户修改签名")
    public Result reviseSign(@Param("sign") String sign) {
        try {
            String appid = Mvcs.getReq().getHeader("appid");
            User user = userService.fetch(appid);
            user.setSign(sysSensitiveService.filter(sign));
            userService.update(user);
            return Result.success("签名修改成功");
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return Result.error("签名修改失败");
    }


    /**
     * 注册
     * @param code 短信验证码
     * @param phone 手机号码
     * @param nickName 注册昵称
     * @param msgId
     * @param password 密码
     * @param inviteCode 邀请码
     * @return
     */
    @ApiOperation(value = "手机号注册", notes = "手机号phone验证码code", httpMethod = "POST", response = Object.class)
    @At("/regPhone")
    @Ok("json")
    @ULog(tag = "用户手机号注册", msg = "手机号: ${phone}, 邀请码: ${inviteCode}")
    public Result regPhone(@ApiParam(name = "code", required = true) @Param("code") String code,
                           @ApiParam @Param("phone") String phone,
                           @ApiParam @Param("nickName") String nickName,
                           @ApiParam @Param("msgId") String msgId,
                           @ApiParam @Param("password") String password,
                           @Param("inviteCode") String inviteCode) throws APIConnectionException, APIRequestException {
        try {
            //判断
            SysParam sysParam = sysParamService.fetch();
            if (!sysParam.isDebug()){
                if (Strings.isEmpty(msgId)) {
                    return Result.error("请先获取验证码");
                }
                if (!userService.checkNickName(nickName)) {
                    return Result.error("昵称不符合规范");
                }
                if (Strings.isBlank(phone)) {
                    return Result.error("手机号码为空");
                }
                if (Strings.isBlank(code)) {
                    return Result.error("验证码为空");
                }
                //校验验证码
                SMSClient client = new SMSClient(sysParam.getJgMasterSecret(), sysParam.getJgAppKey());
                ValidSMSResult res = client.sendValidSMSCode(msgId, code);
                //验证获取号码的手机号与当前手机号是否一致
                if (!Objects.equals(redisService.get("msgId#phone:" + phone), msgId)) {
                    return Result.error("请确保与获取验证码的手机号一致");
                }
            }

            //校验通过,注册成功
            User user = new User();
            int num = userService.count(Cnd.where("phone", "=", Strings.trim(phone)));
            if (num > 0) {
                return Result.error("手机号码已存在!");
            }
            user.setNickName(sysSensitiveService.filter(nickName));
            user.setPassword(password);
            user.setPhone(phone);

            userService.insertUser(user,inviteCode);
            return Result.success("注册成功");
        } catch (Exception e) {
            if (e instanceof IllegalArgumentException || e instanceof APIRequestException) {
                return Result.error("验证码错误");
            }
            if (e instanceof RuntimeException && e.toString().contains("BizException")) {
                throw e;
            }
            log.error(e.getMessage());
            return Result.error("注册失败");
        }
    }


    /**
     * 用户昵称校验
     *
     * @param nickName String 用户昵称
     * @return Result
     */
    @ApiOperation(value = "用户昵称校验", notes = " ", httpMethod = "POST")
    @At("/checkNickName")
    @Ok("json")
    public Result checkNickName(@Param("nickName") String nickName) {
        try {
            return userService.checkNickName(nickName) ? Result.success() : Result.error("昵称不符合规范");
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.error();
        }
    }

    /**
     * 用户重置密码
     *
     * @param phone
     * @param code
     * @param msgId
     * @return
     */
    @ApiOperation(value = "用户重置密码", notes = " ", httpMethod = "POST")
    @At("/resetPassword")
    @Ok("json")
    @ULog(tag = "用户重置密码", msg = "手机号: ${phone}")
    public Result resetPassword(@Param("phone") String phone, @Param("code") String code, @Param("msgId") String msgId, @Param("password") String password) {
        try {
            SysParam sysParam = sysParamService.fetch();
            if (!sysParam.isDebug()) {
                if (Strings.isBlank(phone)) {
                    return Result.error("手机号码为空");
                }
                if (Strings.isBlank(code)) {
                    return Result.error("验证码为空");
                }
                if (Strings.isBlank(msgId)) {
                    return Result.error("请先获取验证码");
                }
                //校验验证码
                SMSClient client = new SMSClient(sysParam.getJgMasterSecret(), sysParam.getJgAppKey());
                ValidSMSResult res = client.sendValidSMSCode(msgId, code);
                //验证获取号码的手机号与当前手机号是否一致
                if (!Objects.equals(redisService.get("msgId#phone:" + phone), msgId)) {
                    return Result.error("请确保与获取验证码的手机号一致");
                }
            }
            User user = userService.fetch(Cnd.where("phone", "=", Strings.trim(phone)));
            if (user == null) {
                return Result.error("该手机号码尚未注册");
            }
            String salt = R.UU32();
            user.setSalt(salt);
            user.setPassword(password);
            user.setPassword(new Sha256Hash(user.getPassword(), ByteSource.Util.bytes(salt), 1024).toHex());
            userService.update(user);
            //重置登录次数
            redisService.del("user:login:"+phone);
            return Result.success("重置密码成功");
        } catch (APIRequestException | IllegalArgumentException ie) {
            return Result.error("验证码错误");
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.error();
        }
    }


    /**
     * 邀请列表
     *
     * @return
     */
    @ApiOperation(value = "邀请列表", notes = " ", httpMethod = "POST")
    @At("/inviteList")
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    public Result inviteList(@Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize) {
        String appid = Mvcs.getReq().getHeader("appid");
        return Result.success(userService.inviteList(appid, pageNumber, pageSize));
    }

    @ApiOperation(value = "游戏大厅权限", notes = " ", httpMethod = "POST")
    @At("/playable")
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    public Result isPlayable() {
        if (!sysParamService.fetch().getGameSwitch()) {
            // 前端大佬要code=0，data为false。
            return Result.success(false);
        }
        String appid = Mvcs.getReq().getHeader("appid");
        return Result.success(userService.fetch(appid).isPlayable());
    }

    @ApiOperation(value = "是否允许发布观点", notes = " ", httpMethod = "POST")
    @At()
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    public Result isCertified() {
        String appid = Mvcs.getReq().getHeader("appid");
        return Result.success(userService.fetch(appid).isCertified());
    }
    /**
     * 划转权限
     *
     * @return
     */
    @ApiOperation(value = "划转", notes = " ", httpMethod = "POST")
    @At("/toTransfer")
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    public Result toTransfer() {
        return Result.success(sysParamService.fetch().getTransferSwitch());
    }

    /**
     * 转入记录权限（收款权限）
     *
     * @return
     */
    @ApiOperation(value = "转入记录权限", notes = " ", httpMethod = "POST")
    @At("/isCollect")
    @Ok("json")
    @Filters({@By(type = ApiTokenFilter.class), @By(type = ParamFilter.class)})
    public Result isCollect() {
        return Result.success(userService.fetch(Mvcs.getReq().getHeader("appid")).isCollect());
    }

    /**
     * 在线人数统计
     */
    @ApiOperation(value = "在线人数统计")
    @At
    @Ok("json")
    public Result onlineCount(EquipmentEnum equipment){
        ScanParams scanParams = new ScanParams();
        scanParams.match("user:token:*"+(equipment==null?"":equipment));
        scanParams.count(Integer.MAX_VALUE);
        //用户id去重
        List<String> result = redisService.scan("0", scanParams).getResult().stream().map(key->{
            String replace = key.replace("user:token:", "");
            return replace.substring(0,replace.indexOf(":"));
        }).distinct().collect(Collectors.toList());
        return Result.success().addData(result.size());
    }

    /**
     * 主播列表
     */
    @ApiOperation(value = "主播列表")
    @At
    @Ok("json")
    public Result anchorList(@Param("pageNumber") int pageNumber,
                             @Param("pageSize") int pageSize){
        return Result.success().addData(userService.anchorList(Mvcs.getReq().getHeader("appid"), pageNumber, pageSize));
    }

}









